//
//  $Id: TCUXMLNode.m 2 2007-09-07 18:35:43Z samson.etienne $
//  TCUCoding
//
//  Created by Simon Stapleton on Fri Oct 18 2002.
//  Copyright (c) 2002 Simon Stapleton. All rights reserved.
//

#import "TCUNumericIDGenerator.h"
#import "NSString+XMLAdditions.h"
//#import "NSDictionary+XMLAdditions.h"

#import "CFRuntime_internal.h"

#import "TCUCFXMLNode.h"

#import "TCUXMLNode.h"


@interface TCUXMLNode (Private)
- (id) initWithType: (TCUXMLNodeType) nodeType dataString: (NSString *) aString info: (void*) info;
- (TCUXMLNodeType) nodeType;
- (id) dataString;
- (void*) infoPointer;
@end

@implementation TCUXMLNode
/*" TCUXMLNode is the head of a class cluster bridging across to CFXMLNode.

Due to the nature of the CoreFoundation bridging process, this class does quite a lot of stamping on the Objective-C runtime, notably in the retain and release process, and anywhere it receives a message it doesn't recognise.  Basically, it dynamically changes its class on the fly, which is a dodgy thing to be doing at the best of times.

As a result, it can be extremely dangerous to refer to objects by their real subclass, as at the time you're referring to them they may not actually have that type.  This complication comes mainly from the fact that if we're casting from CFXMLNode to the bridged class, we don't know at the time of the cast what type we have.  In most cases the object should dynamically recast itself anyway, but it's probably best to refer to them instead as objects of type TCUXMLNode for safety's sake.  The dynamic casting seems to work, even in the pathological case where we have 2 subclasses implementing the same selectors in different ways (see #{-testDynamicallyCasting} in XMLNodeTests.m) but I wouldn't count on it.

In most cases, this should not cause too many problems anyway, as, beyond the generic queries, the nodes themselves are largely useless for user interaction - the interface for querying them falls mainly on the TCUTree class (definitions of the most useful parts of this for XML use are in the XMLAdditions category).  And when programmatically creating nodes, you know the type anyway, so there's little need to worry.

%{I hope.}

Subclassing this class is fraught with hazards due to the way CF allocates its objects.  Don't do it.

"*/

- (CFTypeID) _cfTypeID {
    return CFXMLNodeGetTypeID();
}

+ (void) load
{
    _CFRuntimeInitializeClassForBridging(CFXMLNodeGetTypeID());
    if (_CFRuntimeSetupBridging(CFXMLNodeGetTypeID(), [TCUCFXMLNode class], [TCUCFXMLNode__ class]))
        NSLog(@"Successfully setup bridge for TCUCFXMLNode...");
    else
        NSLog(@"Failed _CFRuntimeSetupBridging()");
}

/*" Create a document node for a given url and string encoding."*/
+ (id) documentNodeWithURL:(NSURL*)url encoding:(NSStringEncoding)encoding
{
    CFXMLDocumentInfo info;
    
    info.sourceURL = (CFURLRef)url;
    info.encoding = CFStringConvertNSStringEncodingToEncoding(encoding);
    
    return [[[TCUXMLNode alloc] initWithType:kCFXMLNodeTypeDocument dataString:nil info:&info] autorelease];
}

/*"Create an element node with the given name and dictionary of attributes.

The keys in the attributes dictionary will be used as the attribute names, and the values will be the values.  The names and values will be created by sending #{-description} to each of the objects. The results of these calls must, of course, conform to XML conventions - if they don't, this method will raise an NSInvalidArgument exception."*/
+ (id) elementNodeWithName:(NSString *)name attributes:(NSDictionary *)attributes
{
    CFXMLElementInfo info;
    NSDictionary * theAttributes = attributes ? attributes : [NSDictionary dictionary];
    
    info.attributes = (CFDictionaryRef)theAttributes;
    info.attributeOrder = (CFArrayRef)[theAttributes allKeys];
    info.isEmpty = YES;
    
    return [[[TCUXMLNode alloc] initWithType:kCFXMLNodeTypeElement dataString:name info:&info] autorelease];
}

+ (id) attributeNode
{
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeAttribute dataString:nil info:NULL] autorelease];
}

/*"Create a processing instruction with the given name and attribute set"*/
+ (id) processingInstructionNodeWithName:(NSString *)name attributes:(NSDictionary*)attributes
{
    CFXMLProcessingInstructionInfo info;
    attributes = attributes ? attributes : [NSDictionary dictionary];
    info.dataString = (CFStringRef)[NSString xmlAttributeStringFromDictionary:attributes];
    
    return [[[TCUXMLNode alloc] initWithType:kCFXMLNodeTypeProcessingInstruction dataString:name info:&info] autorelease];
}

/*"Create a text node containing the given string.

Invalid XML characters will be quoted XML style in the string.  If this node is output and then reparsed, however, those quoted characters will be parsed as entities, so you can't guarantee getting back an identical structure."*/
+ (id) commentNodeWithString:(NSString *)string
{
    id aString = [string stringByQuotingXMLEntities];
    
    return [[[TCUXMLNode alloc] initWithType:kCFXMLNodeTypeComment dataString:aString info:nil] autorelease];
}

/*"Create a text node containing the given string.

Invalid XML characters will be quoted XML style in the string.  If this node is output and then reparsed, however, those quoted characters will be parsed as entities, so you can't guarantee getting back an identical structure."*/
+ (id) textNodeWithString:(NSString *)string
{
    id aString = [string stringByQuotingXMLEntities];
  
    return [[[TCUXMLNode alloc] initWithType:kCFXMLNodeTypeText dataString:aString info:nil] autorelease];
}

/*"Create a CDATA section for the given string"*/
+ (id) cDataNodeWithString:(NSString*)string
{
    id aString = [string stringByQuotingXMLEntities];
    
    return [[[TCUXMLNode alloc] initWithType:kCFXMLNodeTypeCDATASection dataString:aString info:nil] autorelease];
}

+ (id) documentFragmentNode
{
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeDocumentFragment dataString:nil info:NULL] autorelease];
}

+ (id) entityNodeWithName:(NSString*)name /*Misses things here*/
{
    return nil;
}

+ (id) entityReferenceToNodeNamed:(NSString*)name referencedNodeType:(TCUXMLNodeType)type
{
    CFXMLEntityReferenceInfo info;
    info.entityType = type;
    
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeEntityReference dataString:name info:&info] autorelease];
}

/*"Create a doctype node with the given URL, root element and public name (if non-system)"*/
+ (id) documentTypeNodeWithURL: (NSURL*) url
                forRootElement: (NSString*) rootElement
                    publicName: (NSString*) publicName
{
    CFXMLDocumentTypeInfo info;
    info.externalID.systemID = (CFURLRef)url;
    info.externalID.publicID = (CFStringRef) publicName;
    
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeDocumentType dataString:rootElement info:&info] autorelease];
}

+ (id) whitespaceNodeWithString:(NSString*)contents
{
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeWhitespace dataString:contents info:NULL] autorelease];
}

+ (id) notationNodeWithURL:(NSURL*)url name:(NSString*)name publicName:(NSString*)publicName
{
    CFXMLNotationInfo info;
    info.externalID.publicID = (CFStringRef)publicName;
    info.externalID.systemID = (CFURLRef)url;
    
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeNotation dataString:name info:&info] autorelease];
}

+ (id) elementTypeDeclarationNodeWithName:(NSString*)name description:(NSString*)description
{
    CFXMLElementTypeDeclarationInfo info;
    info.contentDescription = (CFStringRef)description;
    
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeElementTypeDeclaration dataString:name info:&info] autorelease];
}

+ (id) attributeListDeclarationNode:(NSString*)name attributes:(NSArray*)attributes
{
    CFXMLAttributeDeclarationInfo *attributeList = calloc([attributes count], sizeof(CFXMLAttributeDeclarationInfo));
    int i;
    for(i = 0; i < [attributes count]; i++) {
        attributeList[i].attributeName = (CFStringRef)[[attributes objectAtIndex:i] valueForKey:@"attributeName"];
        attributeList[i].typeString = (CFStringRef)[[attributes objectAtIndex:i] valueForKey:@"typeString"];
        attributeList[i].defaultString = (CFStringRef)[[attributes objectAtIndex:i] valueForKey:@"defaultString"];
    }
    
    CFXMLAttributeListDeclarationInfo info;
    info.attributes = attributeList;
    info.numberOfAttributes = [attributes count];
    
    return [[[TCUXMLNode alloc] initWithType:TCUXMLNodeTypeAttributeListDeclaration dataString:name info:&info] autorelease];
}

- (id) init {
    [self autorelease];
    return nil;
}

/*"Designated intializer - note that the default initializer is explicitly disallowed"*/
- (id) initWithType: (TCUXMLNodeType) nodeType dataString:(NSString *)aString info:(void*)info
{
    self = (TCUXMLNode*) CFXMLNodeCreate (NULL, nodeType, (CFStringRef)aString, info, kCFXMLNodeCurrentVersion);
    return self;
}

- (id) initWithCoder:(NSCoder*)coder
{
    
    return [self initWithType:[coder decodeIntForKey:@"nodeType"]
                   dataString:[coder decodeObjectForKey:@"dataString"]
                         info:[coder decodeObjectForKey:@"infoPointer"]];
}

- (void) encodeWithCoder:(NSCoder*)coder
{
    [coder encodeInt:[self nodeType] forKey:@"nodeType"];
    [coder encodeObject:[self dataString] forKey:@"dataString"];
    [coder encodeObject:[self infoPointer] forKey:@"infoPointer"];
}

- (id) copyWithZone:(NSZone*)zone
{
    return (TCUXMLNode*)CFXMLNodeCreateCopy(NULL,(CFXMLNodeRef)self);
}

- (id) dataString
{
    return (id)CFXMLNodeGetString((CFXMLNodeRef)self);
}

- (void*) infoPointer
{
    return (void*)CFXMLNodeGetInfoPtr((CFXMLNodeRef)self);
}

- (TCUXMLNodeType) nodeType
{
    return (TCUXMLNodeType)CFXMLNodeGetTypeCode((CFXMLNodeRef)self);
}

@end